package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/gin-gonic/gin/testdata/protoexample"

	"github.com/gin-gonic/gin"
)

var secrets = gin.H{
	"foo":    gin.H{"email": "foo@bar.com", "phone": "123433"},
	"austin": gin.H{"email": "austin@example.com", "phone": "666"},
	"lena":   gin.H{"email": "lena@guapa.com", "phone": "523443"},
}

type Person struct {
	Name    string `form:"name"`
	Address string `form:"address"`
}

func startPage(c *gin.Context) {
	var person Person
	if c.ShouldBindQuery(&person) == nil {
		log.Println("====== Only Bind By Query String ======")
		log.Println(person.Name)
		log.Println(person.Address)
	}
	c.String(200, "Success")
}

type Login struct {
	User     string `form:"user" json:"user" xml:"user"  binding:"required"`
	Password string `form:"password" json:"password" xml:"password" binding:"required"`
}

func main() {
	r := gin.Default()

	// 快速入门
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "pong",
		})
	})

	// AsciiJson
	r.GET("/someAsciiJson", func(c *gin.Context) {
		data := map[string]interface{}{
			"lang": "GO语言",
			"tag":  "<br>",
		}

		// 输出 : {"lang":"GO\u8bed\u8a00","tag":"\u003cbr\u003e"}
		c.AsciiJSON(http.StatusOK, data)
	})

	// HTML
	r.LoadHTMLGlob("src/999.gin/templates/*")
	//router.LoadHTMLFiles("templates/template1.html", "templates/template2.html")
	r.GET("/index", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.tmpl", gin.H{
			"title": "Main website",
		})
	})

	// Query 和 post form
	r.POST("/post", func(c *gin.Context) {

		id := c.Query("id")
		page := c.DefaultQuery("page", "0")
		name := c.PostForm("name")
		message := c.PostForm("message")

		fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message)
	})

	// XML/JSON/YAML/ProtoBuf 渲染
	{
		// gin.H 是 map[string]interface{} 的一种快捷方式
		r.GET("/someJSON", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
		})

		r.GET("/moreJSON", func(c *gin.Context) {
			// 你也可以使用一个结构体
			var msg struct {
				Name    string `json:"user"`
				Message string
				Number  int
			}
			msg.Name = "Lena"
			msg.Message = "hey"
			msg.Number = 123
			// 注意 msg.Name 在 JSON 中变成了 "user"
			// 将输出：{"user": "Lena", "Message": "hey", "Number": 123}
			c.JSON(http.StatusOK, msg)
		})

		r.GET("/someXML", func(c *gin.Context) {
			c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
		})

		r.GET("/someYAML", func(c *gin.Context) {
			c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
		})

		r.GET("/someProtoBuf", func(c *gin.Context) {
			reps := []int64{int64(1), int64(2)}
			label := "test"
			// protobuf 的具体定义写在 testdata/protoexample 文件中。
			data := &protoexample.Test{
				Label: &label,
				Reps:  reps,
			}
			// 请注意，数据在响应中变为二进制数据
			// 将输出被 protoexample.Test protobuf 序列化了的数据
			c.ProtoBuf(http.StatusOK, data)
		})
	}

	// 使用 BasicAuth 中间件
	{
		// 路由组使用 gin.BasicAuth() 中间件
		// gin.Accounts 是 map[string]string 的一种快捷方式
		authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{
			"foo":    "bar",
			"austin": "1234",
			"lena":   "hello2",
			"manu":   "4321",
		}))

		// /admin/secrets 端点
		// 触发 "localhost:8080/admin/secrets
		authorized.GET("/secrets", func(c *gin.Context) {
			// 获取用户，它是由 BasicAuth 中间件设置的
			user := c.MustGet(gin.AuthUserKey).(string)
			if secret, ok := secrets[user]; ok {
				c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret})
			} else {
				c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("})
			}
		})
	}

	// 从 reader 读取数据
	r.GET("/someDataFromReader", func(c *gin.Context) {
		response, err := http.Get("https://raw.githubusercontent.com/gin-gonic/logo/master/color.png")
		if err != nil || response.StatusCode != http.StatusOK {
			c.Status(http.StatusServiceUnavailable)
			return
		}

		reader := response.Body
		contentLength := response.ContentLength
		contentType := response.Header.Get("Content-Type")

		extraHeaders := map[string]string{
			"Content-Disposition": `attachment; filename="gopher.png"`,
		}

		c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders)
	})

	// 只绑定 url 查询字符串
	r.Any("/testing", startPage)

	// 查询字符串参数
	r.GET("/welcome", func(c *gin.Context) {
		firstname := c.DefaultQuery("firstname", "Guest")
		lastname := c.Query("lastname") // c.Request.URL.Query().Get("lastname") 的一种快捷方式

		c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
	})

	// 模型绑定和验证
	{
		// 绑定 JSON ({"user": "manu", "password": "123"})
		r.POST("/loginJSON", func(c *gin.Context) {
			var json Login
			if err := c.ShouldBindJSON(&json); err != nil {
				c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
				return
			}

			if json.User != "manu" || json.Password != "123" {
				c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
				return
			}

			c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
		})

		// 绑定 XML (
		//	<?xml version="1.0" encoding="UTF-8"?>
		//	<root>
		//		<user>manu</user>
		//		<password>123</password>
		//	</root>)
		r.POST("/loginXML", func(c *gin.Context) {
			var xml Login
			if err := c.ShouldBindXML(&xml); err != nil {
				c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
				return
			}

			if xml.User != "manu" || xml.Password != "123" {
				c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
				return
			}

			c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
		})

		// 绑定 HTML 表单 (user=manu&password=123)
		r.POST("/loginForm", func(c *gin.Context) {
			var form Login
			// 根据 Content-Type Header 推断使用哪个绑定器。
			if err := c.ShouldBind(&form); err != nil {
				c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
				return
			}

			if form.User != "manu" || form.Password != "123" {
				c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
				return
			}

			c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
		})
	}

	// 设置和获取 Cookie
	r.GET("/cookie", func(c *gin.Context) {
		cookie, err := c.Cookie("gin_cookie")

		if err != nil {
			cookie = "NotSet"
			c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true)
		}

		fmt.Printf("Cookie value: %s \n", cookie)
	})

	// 路由参数
	{
		// 此 handler 将匹配 /user/john 但不会匹配 /user/ 或者 /user
		r.GET("/user/:name", func(c *gin.Context) {
			name := c.Param("name")
			c.String(http.StatusOK, "Hello %s", name)
		})

		// 此 handler 将匹配 /user/john/ 和 /user/john/send
		// 如果没有其他路由匹配 /user/john，它将重定向到 /user/john/
		r.GET("/user/:name/*action", func(c *gin.Context) {
			name := c.Param("name")
			action := c.Param("action")
			message := name + " is " + action
			c.String(http.StatusOK, message)
		})
	}

	// 路由组
	{
		// 简单的路由组: v1
		v1 := r.Group("/v1")
		{
			v1.POST("/login", loginEndpoint)
			v1.POST("/submit", submitEndpoint)
			v1.POST("/read", readEndpoint)
		}

		// 简单的路由组: v2
		v2 := r.Group("/v2")
		{
			v2.POST("/login", loginEndpoint)
			v2.POST("/submit", submitEndpoint)
			v2.POST("/read", readEndpoint)
		}
	}

	// 静态文件服务
	//r.Static("/assets", "./assets")
	//r.StaticFS("/more_static", http.Dir("my_file_system"))
	//r.StaticFile("/favicon.ico", "./resources/favicon.ico")

	// 静态资源嵌入
	//{
	//	t, err := loadTemplate()
	//	if err != nil {
	//		panic(err)
	//	}
	//	r.SetHTMLTemplate(t)
	//
	//	r.GET("/", func(c *gin.Context) {
	//		c.HTML(http.StatusOK, "/html/index.tmpl", nil)
	//	})
	//}

	r.Run() // 监听并在 0.0.0.0:8080 上启动服务
}

func loginEndpoint(context *gin.Context) {
	message := context.Query("message")
	fmt.Printf("loginEndpoint  message: %s\n", message)
}

func submitEndpoint(context *gin.Context) {
	message := context.Query("message")
	fmt.Printf("submitEndpoint  message: %s\n", message)
}

func readEndpoint(context *gin.Context) {
	message := context.Query("message")
	fmt.Printf("readEndpoint  message: %s\n", message)
}

//// loadTemplate 加载由 go-assets-builder 嵌入的模板
//func loadTemplate() (*template.Template, error) {
//	t := template.New("")
//	for name, file := range Assets.Files {
//		if file.IsDir() || !strings.HasSuffix(name, ".tmpl") {
//			continue
//		}
//		h, err := ioutil.ReadAll(file)
//		if err != nil {
//			return nil, err
//		}
//		t, err = t.New(name).Parse(string(h))
//		if err != nil {
//			return nil, err
//		}
//	}
//	return t, nil
//}
